#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>

#define BUFFER_SIZE 1024          // 定义缓冲区大小
#define PORT 6782                 // 定义服务器监听端口
#define BASE_DIR "/mnt/e/share/7-项目二"    // 定义服务器共享文件夹路径

// 定义客户端节点结构体
typedef struct ClientNode { 
    char ip[16];                  // 客户端IP地址
    int listen_port;              // 客户端监听端口
    struct sockaddr_in addr;      // 客户端地址
    char client_folder_path[512]; // 客户端文件夹路径
    struct ClientNode *next;      // 指向下一个客户端节点的指针
} ClientNode;

ClientNode *client_list = NULL;   // 客户端链表头指针
pthread_mutex_t list_mutex = PTHREAD_MUTEX_INITIALIZER; // 客户端链表互斥锁，用于线程安全
int server_running = 1;           // 服务器运行标志
int listen_socket;                // 服务器监听套接字

// 添加客户端到链表的函数
void add_client(const char *ip, int listen_port, struct sockaddr_in addr, const char *folder_path) {    
    pthread_mutex_lock(&list_mutex);    // 加锁，保证线程安全
    ClientNode *new_node = malloc(sizeof(ClientNode));  // 创建新的客户端节点
    strcpy(new_node->ip, ip);           // 复制客户端IP地址
    new_node->listen_port = listen_port;    // 设置客户端监听端口
    new_node->addr = addr;              // 设置客户端地址
    strncpy(new_node->client_folder_path, folder_path, sizeof(new_node->client_folder_path) - 1); // 复制客户端文件夹路径
    new_node->next = client_list;       // 将新节点插入到链表头部
    client_list = new_node;             // 更新链表头指针
    pthread_mutex_unlock(&list_mutex);  // 解锁
}

// 查找客户端节点的函数
ClientNode *find_client_node(int listen_port) { 
    pthread_mutex_lock(&list_mutex);    // 加锁，保证线程安全
    for (ClientNode *curr = client_list; curr; curr = curr->next)    // 遍历链表
        if (curr->listen_port == listen_port) {    // 找到匹配的节点
            pthread_mutex_unlock(&list_mutex);    // 解锁
            return curr;
        }
    pthread_mutex_unlock(&list_mutex);  // 解锁
    return NULL;
}

// 发送文件给客户端的函数
int send_file_to_client(ClientNode *node, const char *filename, const char *src_folder) {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);  // 创建套接字
    struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(node->listen_port) };  // 初始化客户端地址结构体
    inet_pton(AF_INET, node->ip, &addr.sin_addr);  // 将IP地址从文本形式转换为二进制形式
    if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {  // 连接客户端
        perror("连接客户端失败");
        close(sockfd);
        return -1;
    }

    char path[1024];
    snprintf(path, sizeof(path), "%s/%s", src_folder, filename);  // 构造文件路径
    FILE *fp = fopen(path, "rb");  // 打开文件
    if (!fp) {
        perror("打开文件失败");
        close(sockfd);
        return -1;
    }

    fseek(fp, 0, SEEK_END);  // 获取文件大小
    size_t size = ftell(fp);
    rewind(fp);

    uint32_t name_len = strlen(filename);  // 获取文件名长度和文件大小
    uint32_t file_size = size;

    send(sockfd, &name_len, sizeof(name_len), 0);  // 发送文件名长度
    send(sockfd, &file_size, sizeof(file_size), 0);  // 发送文件大小
    send(sockfd, filename, name_len, 0);  // 发送文件名

    char buf[BUFFER_SIZE];  // 缓冲区
    size_t n;
    while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)  // 循环读取文件并发送
        send(sockfd, buf, n, 0);

    fclose(fp);  // 关闭文件
    close(sockfd);  // 关闭套接字
    printf("文件 %s 已从 %s 发送给客户端 %d\n", filename, src_folder, node->listen_port);  // 打印发送信息
    return 0;
}

// 发送文件给所有客户端的函数
void send_file_to_all_clients(const char *filename) {
    char src_folder[512], path[1024];  // 源文件夹路径
    int found = 0;  // 文件是否找到的标志

    pthread_mutex_lock(&list_mutex);  // 加锁，保证线程安全
    for (ClientNode *curr = client_list; curr; curr = curr->next) {  // 遍历客户端链表
        snprintf(src_folder, sizeof(src_folder), "%s/server_file/%d", BASE_DIR, curr->listen_port);  // 构造源文件夹路径
        snprintf(path, sizeof(path), "%s/%s", src_folder, filename);  // 构造文件路径
        if (access(path, F_OK) == 0) {  // 检查文件是否存在
            found = 1;
            break;
        }
    }
    if (!found) {  // 如果文件未找到
        printf("未找到文件 %s 在任何客户端文件夹中\n", filename);
        pthread_mutex_unlock(&list_mutex);  // 解锁
        return;
    }

    for (ClientNode *curr = client_list; curr; curr = curr->next)  // 遍历客户端链表，发送文件
        send_file_to_client(curr, filename, src_folder);

    pthread_mutex_unlock(&list_mutex);  // 解锁
}

// 从指定源文件夹发送文件给所有客户端的函数
void send_file_to_all_clients_from_src(const char *filename, const char *src_folder) {
    pthread_mutex_lock(&list_mutex);  // 加锁，保证线程安全
    for (ClientNode *curr = client_list; curr; curr = curr->next)  // 遍历客户端链表，发送文件
        send_file_to_client(curr, filename, src_folder);
    pthread_mutex_unlock(&list_mutex);  // 解锁
}

// 列出指定目录下文件的函数
void list_files_in_directory(const char *dir_path) {
    DIR *dir = opendir(dir_path);  // 打开目录
    if (!dir) {
        perror("打开目录失败");
        return;
    }
    struct dirent *entry;  // 目录项结构体指针
    printf("文件列表:\n");
    while ((entry = readdir(dir)) != NULL)  // 循环读取目录项
        if (entry->d_type == DT_REG)  // 如果是普通文件
            printf("- %s\n", entry->d_name);
    closedir(dir);  // 关闭目录
}

// 客户端处理线程函数
void *client_handler(void *arg) {
    int clientfd = ((int *)arg)[0];  // 获取客户端套接字
    struct sockaddr_in caddr = *(struct sockaddr_in *)(((int *)arg) + 1);  // 获取客户端地址
    free(arg);  // 释放参数内存

    char buf[BUFFER_SIZE];  // 缓冲区
    recv(clientfd, buf, sizeof(buf), 0);  // 接收客户端消息
    char *line1 = strtok(buf, "\n");  // 分割消息，获取第一行
    char *line2 = strtok(NULL, "\n");  // 分割消息，获取第二行
    if (!line1 || !line2) {  // 如果消息格式不正确
        close(clientfd);
        pthread_exit(NULL);
    }

    int listen_port = atoi(line1);  // 获取客户端监听端口
    char folder_path[512];  // 客户端文件夹路径
    strncpy(folder_path, line2, sizeof(folder_path) - 1);  // 复制客户端文件夹路径

    char server_folder[512];  // 服务器端为该客户端创建的文件夹路径
    snprintf(server_folder, sizeof(server_folder), "%s/server_file/%d", BASE_DIR, listen_port);  // 构造服务器端文件夹路径
    mkdir(server_folder, 0755);  // 创建服务器端文件夹

    add_client(inet_ntoa(caddr.sin_addr), listen_port, caddr, folder_path);  // 将客户端添加到链表

    while (1) {  // 循环接收客户端文件
        uint32_t name_len, file_size;  // 文件名长度和文件大小

        ssize_t n = recv(clientfd, &name_len, sizeof(name_len), 0);  // 接收文件名长度
        if (n <= 0) break;

        n = recv(clientfd, &file_size, sizeof(file_size), 0);  // 接收文件大小
        if (n <= 0) break;

        char filename[256];  // 文件名
        n = recv(clientfd, filename, name_len, 0);  // 接收文件名
        if (n <= 0) break;
        filename[name_len] = '\0';  // 添加字符串结束符

        char *file_data = malloc(file_size);  // 分配内存用于存储文件数据
        size_t total = 0;
        while (total < file_size) {  // 循环接收文件数据
            n = recv(clientfd, file_data + total, file_size - total, 0);
            if (n <= 0) break;
            total += n;
        }

        char full_path[1024];  // 构造文件完整路径
        snprintf(full_path, sizeof(full_path), "%s/%s", server_folder, filename);
        int fd = open(full_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);  // 打开文件
        write(fd, file_data, file_size);  // 写入文件数据
        close(fd);  // 关闭文件
        free(file_data);  // 释放内存

        printf("已保存文件: %s (%u bytes)\n", full_path, file_size);  // 打印保存文件信息
    }

    close(clientfd);  // 关闭客户端套接字
    pthread_exit(NULL);  // 退出线程
}

// 接受客户端连接的线程函数
void *accept_client_thread(void *arg) {
    int listenfd = *(int *)arg;  // 获取监听套接字
    while (1) {  // 循环接受客户端连接
        struct sockaddr_in caddr;  // 客户端地址结构体
        socklen_t len = sizeof(caddr);
        int clientfd = accept(listenfd, (struct sockaddr *)&caddr, &len);  // 接受客户端连接
        if (clientfd < 0) {
            perror("接受客户端连接失败");
            continue;
        }

        void *args = malloc(sizeof(int) + sizeof(struct sockaddr_in));  // 分配内存用于传递参数
        ((int *)args)[0] = clientfd;  // 存储客户端套接字
        memcpy(((int *)args) + 1, &caddr, sizeof(caddr));  // 复制客户端地址

        pthread_t tid;  // 线程ID
        if (pthread_create(&tid, NULL, client_handler, args) != 0) {  // 创建线程处理客户端连接
            perror("创建线程失败");
            free(args);
            close(clientfd);
        } else {
            pthread_detach(tid);  // 分离线程
        }
    }
    return NULL;
}

int main() {
    signal(SIGINT, (__sighandler_t)exit);  // 处理中断信号，退出程序

    mkdir(BASE_DIR, 0755);  // 创建基础目录
    char path[512];  // 服务器文件目录路径
    snprintf(path, sizeof(path), "%s/server_file", BASE_DIR);  // 构造服务器文件目录路径
    mkdir(path, 0755);  // 创建服务器文件目录

    int listenfd = socket(AF_INET, SOCK_STREAM, 0);  // 创建监听套接字
    struct sockaddr_in addr = { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(PORT) };  // 服务器地址结构体
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));  // 设置套接字选项，允许地址重用
    bind(listenfd, (struct sockaddr *)&addr, sizeof(addr));  // 绑定地址
    listen(listenfd, 5);  // 开始监听

    printf("服务器启动成功，监听端口 %d\n", PORT);  // 打印服务器启动信息

    pthread_t accept_thread;  // 线程ID
    pthread_create(&accept_thread, NULL, accept_client_thread, &listenfd);  // 创建接受客户端连接的线程
    pthread_detach(accept_thread);  // 分离线程

    while (1) {  // 主循环，处理用户输入
        int choice;
        char filename[BUFFER_SIZE];
        int listen_port;

        printf("\n请选择操作:\n");  // 打印操作菜单
        printf("1. 传输文件给所有客户端\n");
        printf("2. 传输文件给指定客户端\n");
        printf("3. 传输文件夹内所有文件给指定客户端\n");
        printf("4. 传输文件夹内所有文件给所有客户端\n");
        printf("5. 退出\n");
        printf("输入选择: ");

        if (scanf("%d", &choice) != 1) {  // 读取用户输入
            while (getchar() != '\n');
            continue;
        }

        switch (choice) {  // 根据用户选择执行相应操作
            case 1:
                printf("请输入要传输的文件名: ");
                scanf("%s", filename);
                send_file_to_all_clients(filename);
                break;
            case 2: {
                int src_port;
                printf("请输入源客户端的监听端口（文件来源）: ");
                if (scanf("%d", &src_port) != 1) {
                    while (getchar() != '\n');
                    continue;
                }

                char src_folder[512];  // 源文件夹路径
                snprintf(src_folder, sizeof(src_folder), "%s/server_file/%d", BASE_DIR, src_port);  // 构造源文件夹路径
                list_files_in_directory(src_folder);  // 列出源文件夹中的文件

                printf("请输入要传输的文件名: ");
                scanf("%s", filename);

                printf("请输入目标客户端的监听端口: ");
                scanf("%d", &listen_port);

                ClientNode *dst_node = find_client_node(listen_port);  // 查找目标客户端节点
                if (!dst_node) {
                    printf("未找到目标客户端 %d\n", listen_port);
                    break;
                }

                send_file_to_client(dst_node, filename, src_folder);  // 发送文件给目标客户端
                break;
            }
            case 3: {
                int src_port;
                printf("请输入源客户端的监听端口（文件来源）: ");
                if (scanf("%d", &src_port) != 1) {
                    while (getchar() != '\n');
                    continue;
                }

                char src_folder[512];  // 源文件夹路径
                snprintf(src_folder, sizeof(src_folder), "%s/server_file/%d", BASE_DIR, src_port);  // 构造源文件夹路径

                DIR *dir = opendir(src_folder);  // 打开源文件夹
                if (!dir) {
                    printf("无法打开源文件夹 %s\n", src_folder);
                    break;
                }

                printf("请输入目标客户端的监听端口: ");
                scanf("%d", &listen_port);

                ClientNode *dst_node = find_client_node(listen_port);  // 查找目标客户端节点
                if (!dst_node) {
                    printf("未找到目标客户端 %d\n", listen_port);
                    closedir(dir);
                    break;
                }

                struct dirent *entry;  // 目录项结构体指针
                while ((entry = readdir(dir)) != NULL) {  // 循环读取目录项
                    if (entry->d_type == DT_REG) {  // 如果是普通文件
                        send_file_to_client(dst_node, entry->d_name, src_folder);  // 发送文件给目标客户端
                    }
                }
                closedir(dir);  // 关闭目录
                break;
            }
            case 4: {
                int src_port;
                printf("请输入源客户端的监听端口（文件来源）: ");
                if (scanf("%d", &src_port) != 1) {
                    while (getchar() != '\n');
                    continue;
                }

                char src_folder[512];  // 源文件夹路径
                snprintf(src_folder, sizeof(src_folder), "%s/server_file/%d", BASE_DIR, src_port);  // 构造源文件夹路径

                DIR *dir = opendir(src_folder);  // 打开源文件夹
                if (!dir) {
                    printf("无法打开源文件夹 %s\n", src_folder);
                    break;
                }

                struct dirent *entry;  // 目录项结构体指针
                while ((entry = readdir(dir)) != NULL) {  // 循环读取目录项
                    if (entry->d_type == DT_REG) {  // 如果是普通文件
                        send_file_to_all_clients_from_src(entry->d_name, src_folder);  // 发送文件给所有客户端
                    }
                }
                closedir(dir);  // 关闭目录
                break;
            }
            case 5:
                exit(0);  // 退出程序
            default:
                printf("无效的选择\n");
        }
    }

    return 0;
}
